﻿using System.Configuration;
using System.Diagnostics;
using System.Net.Sockets;
using System.Reflection;
using OOP.IOC.lib.Attr;
using OOP.IOC.lib.Base;
using OOP.IOC.lib.Enums;

namespace OOP.IOC.lib
{
    public class IOCContainer
    {
        public static Dictionary<string, ClassDefinition> Definitions = new Dictionary<string, ClassDefinition>();

        private static Dictionary<string, object> SingletonCache = new Dictionary<string, object>();
        private static Dictionary<string, object> EarlySingletonCache = new Dictionary<string, object>();

        public static void Init()
        {
            Type[] types = Assembly.Load("WpfApp1.Service").GetTypes();
            Type[] types2 = Assembly.Load("WpfApp1.DAL").GetTypes();
            List<Type> allTypes = new List<Type>();
            allTypes.AddRange(types.Where(t => t.Name.Contains("Service")));
            allTypes.AddRange(types2.Where(t => t.Name.Contains("DbContext")));
            CollectionDefinition(allTypes);
            List<ClassDefinition> classDefinitions = Definitions.Values.ToList();
            CreateInstances(classDefinitions);
        }

        /// <summary>
        /// 收集实体类的定义信息
        /// </summary>
        /// <param name="dbTypes"></param>
        private static void CollectionDefinition(List<Type> dbTypes)
        {
            foreach (Type type in dbTypes)
            {
                if (type.IsInterface)
                    continue;
                ClassDefinition definition = new ClassDefinition();
                Type[] types = type.GetInterfaces();
                string fullName;
                if (types.Length > 0)
                {
                    fullName = types.Where(t => t.Name.Contains("Service") && !t.IsGenericType).FirstOrDefault().FullName;
                }
                else
                {
                    fullName = type.FullName;
                }
                definition.ClassName = fullName;
                definition.Type = type;

                DbSourceAttribute? attr = type.GetCustomAttribute<DbSourceAttribute>();
                if (attr == null)
                {
                    definition.Protocal = ProtocalType.Singleton;
                }
                else
                {
                    definition.Protocal = attr.Protocal;
                }
                Definitions.Add(fullName, definition);
            }
        }

        /// <summary>
        /// 根据类信息创建单例实例，并添加到一级缓存中
        /// </summary>
        /// <param name="definitions"></param>
        private static void CreateInstances(List<ClassDefinition> definitions)
        {
            foreach (var definiton in definitions)
            {
                if (definiton.Protocal == ProtocalType.Singleton)
                {
                    if (definiton.IsDbType)
                    {
                        CreateDbInfo(definiton,null);
                    }
                    else
                    {
                        CreateInstance(definiton);
                    }
                }
            }
        }

        /// <summary>
        /// 创建单个实例
        /// </summary>
        /// <param name="definitions"></param>
        public static object CreateInstance(ClassDefinition definition)
        {
            try
            {
                string className = definition.ClassName;
                // 先判断是否在一级缓存中已经创建
                // 这里的作用就是避免某些因为属性依赖被创建的实例被再次创建
                if (SingletonCache.ContainsKey(className))
                    return SingletonCache[className];

                object? instance = Activator.CreateInstance(definition.Type);
                EarlySingletonCache.Add(className, instance);

                // 解析依赖
                PropertyInfo[] propertyInfos = definition.Type.GetProperties().Where(p => p.GetCustomAttribute<AutowireAttribute>() != null).ToArray();
                if (propertyInfos.Length > 0)
                {
                    foreach (var prop in propertyInfos)
                    {
                        string? fullName = prop.PropertyType.FullName;
                        if (SingletonCache.ContainsKey(fullName))
                        {
                            prop.SetValue(instance, SingletonCache[fullName]);
                            continue;
                        }
                        else if (EarlySingletonCache.ContainsKey(fullName))
                        {
                            prop.SetValue(instance, EarlySingletonCache[fullName]);
                            continue;
                        }
                        else
                        {
                            ClassDefinition classDefinition = Definitions[fullName];
                            prop.SetValue(instance, CreateInstance(classDefinition));
                        }
                    }
                }
                SingletonCache[className] = instance;
                EarlySingletonCache.Remove(className);
                return instance;
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex);
            }
            return null;
        }

        /// <summary>
        /// 创建 数据库访问类 实例
        /// </summary>
        /// <param name="classDefinition"></param>
        private static void CreateDbInfo(ClassDefinition classDefinition, List<string> dbNames)
        {
            if (dbNames == null || dbNames.Count == 0) return;
            Type dbType = classDefinition.Type;
            DbSourceAttribute? attr = dbType.GetCustomAttribute<DbSourceAttribute>();
            string configId;
            if (attr == null || string.IsNullOrEmpty(attr.DbName))
            {
                configId = dbNames[0];
            }
            else
            {
                configId = attr.DbName;
            }
            string? fullName = dbType.FullName;
            ConstructorInfo? constructorInfo = dbType.GetConstructor(new Type[] { typeof(string) });
            object instance = null;
            if (constructorInfo == null)
                instance = Activator.CreateInstance(dbType);
            instance = constructorInfo.Invoke(new object[] { fullName });
            SingletonCache[fullName] = instance;
        }

        public static T GetInstance<T>()
        {
            string? name = typeof(T).FullName;

            if (SingletonCache.ContainsKey(name))
                return (T)SingletonCache[name];
            else if (Definitions.ContainsKey(name))
            {
                return (T)GetBean(Definitions[name]);
            }
            else
            {
                throw new Exception($"找不到对应的实例信息，请检查 {name} 是否注册");
            }
        }

        private static object GetBean(ClassDefinition definition)
        {
            if (definition.Protocal == ProtocalType.Singleton)
            {

            }
            else
            {

            }
            return null;
        }

        public static void Clear()
        {
            SingletonCache.Clear();
        }
    }
}
